﻿using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        WriteableBitmap wrBmp;
        BitmapSource bmpS;
        byte[] pixelData;
        byte[] opixelData;
        int w;
        int h;
        int widthInBytes;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button1_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog op = new OpenFileDialog();
            op.Title = "Select a picture";
            op.Filter = "All supported graphics|*.jpg;*.jpeg;*.png;*.bmp|" +
                "JPEG (*.jpg;*.jpeg)|*.jpg;*.jpeg|" +
                "Portable Network Graphic (*.png)|*.png";
            if (op.ShowDialog() == true)
            {
                bmpS = new BitmapImage(new Uri(op.FileName));
                Img1.Source = bmpS;
                slider1.Value = 0;
                slider2.Value = 0;

                BitmapSource prgbaSource = new FormatConvertedBitmap(bmpS, PixelFormats.Pbgra32, null, 0);
                wrBmp = new WriteableBitmap(prgbaSource);
                w = wrBmp.PixelWidth;
                h = wrBmp.PixelHeight;
                pixelData = new byte[w * h * 4];
                opixelData = new byte[w * h * 4];
                widthInBytes = 4 * w;
                wrBmp.CopyPixels(pixelData, widthInBytes, 0);
                wrBmp.CopyPixels(opixelData, widthInBytes, 0);
                wrBmp.WritePixels(new Int32Rect(0, 0, w, h), pixelData, widthInBytes, 0);
                Img2.Source = wrBmp;

                if (hist.IsChecked == true)
                {
                    histogram(hist1, opixelData);
                    histogram(hist2, pixelData);
                }

                res.Content = w + " x " + h;
//                oblPodobienstwa(opixelData, pixelData, pod1);
            }

        }

        private void kopiowanie()
        {
            opixelData.CopyTo(pixelData, 0);
        }

        private void jasnosc()
        {
            if (slider1.Value >= 0)
            {
                for (int i = 0; i < pixelData.Length; ++i)
                {
                    if (i % 4 == 3)
                        continue;
                    pixelData[i] = (pixelData[i] + slider1.Value < 255) ? (byte)(pixelData[i] + slider1.Value) : (byte)255;
                }
            }
            else
            {
                for (int i = 0; i < pixelData.Length; ++i)
                {
                    if (i % 4 == 3)
                        continue;
                    pixelData[i] = (pixelData[i] + slider1.Value > 0) ? (byte)(pixelData[i] + slider1.Value) : (byte)0;

                }
            }
        }

        private void oblPodobienstwa(byte[] otab, byte[] tab, Label label)
        {
            double wynik = 0;
            int dzielenie = 0;
            for (int i = 0; i < otab.Length; i++)
            {
                if (i % 4 != 3)
                {
                    dzielenie++;
                    wynik += Math.Pow((double)((int)otab[i] - (int)tab[i]), 2);
                }
            }
            wynik = wynik / dzielenie;
            label.Content = wynik;
        }

        private void kontrast()
        {
            float factor = (float)(255 * (slider2.Value + 255)) / (float)(255 * (255 - slider2.Value));
            for (int i = 0; i < pixelData.Length; ++i)
            {
                if (i % 4 == 3)
                    continue;
                int wynik = (int)(factor * (pixelData[i] - 128) + 128);
                pixelData[i] = (wynik < 0) ? (byte)0 : (wynik > 255) ? (byte)255 : (byte)wynik;

            }
        }

        private void histogram(Canvas cnv, byte[] tab)
        {
            cnv.Background = new SolidColorBrush(Color.FromArgb(150, 0, 0, 0));
            cnv.Children.Clear();
            int[] hisTab = new int[256];
            int[] rTab = new int[256];
            int[] gTab = new int[256];
            int[] bTab = new int[256];

            for (int i = 0; i < hisTab.Length; i++)
            {
                hisTab[i] = 0;
                rTab[i] = 0;
                gTab[i] = 0;
                bTab[i] = 0;
            }

            for (int i = 0; i < tab.Length; i++)
            {
                if (i % 4 == 3)
                    continue;
                else if (i % 4 == 2)
                    bTab[tab[i]]++;
                else if (i % 4 == 1)
                    gTab[tab[i]]++;
                else if (i % 4 == 0)
                    rTab[tab[i]]++;
                hisTab[tab[i]]++;
            }

            int max = hisTab[0];
            for (int i = 0; i < 256; i++)
            {
                if (hisTab[i] > max)
                    max = hisTab[i];
            }

            float wsp = 1;
            if (max > 100)
                wsp = 1 - ((max - 100) / (float)max);
            else if (max < 100)
                wsp = 1 + ((100 - max) / 100);

            for (int i = 0; i < 255; i++)
            {
                Line line = new Line();
                line.X1 = i;
                line.X2 = i;
                line.Y1 = 100;
                line.Y2 = 100 - (int)(hisTab[i] * wsp);
                line.StrokeThickness = 1;
                line.Stroke = Brushes.White;
                cnv.Children.Add(line);

                if (rgbHist.IsChecked == true)
                {
                    Line rline = new Line();
                    rline.X1 = i;
                    rline.X2 = i;
                    rline.Y1 = 100;
                    rline.Y2 = 100 - (int)(rTab[i] * wsp);
                    rline.StrokeThickness = 1;
                    rline.Stroke = Brushes.Red;
                    cnv.Children.Add(rline);

                    Line gline = new Line();
                    gline.X1 = i;
                    gline.X2 = i;
                    gline.Y1 = 100;
                    gline.Y2 = 100 - (int)(gTab[i] * wsp);
                    gline.StrokeThickness = 1;
                    gline.Stroke = Brushes.Green;
                    cnv.Children.Add(gline);

                    Line bline = new Line();
                    bline.X1 = i;
                    bline.X2 = i;
                    bline.Y1 = 100;
                    bline.Y2 = 100 - (int)(bTab[i] * wsp);
                    bline.StrokeThickness = 1;
                    bline.Stroke = Brushes.Blue;
                    cnv.Children.Add(bline);
                }
            }
        }


        private void wyswietlenie()
        {
            jasnosc();
            kontrast();
            wrBmp.WritePixels(new Int32Rect(0, 0, w, h), pixelData, widthInBytes, 0);
            Img2.Source = wrBmp;

            if (hist.IsChecked == true)
                histogram(hist2, pixelData);

            oblPodobienstwa(opixelData, pixelData, pod2);

        }

        private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            slider1l.Content = "Jasność: " + (int)slider1.Value;
            kopiowanie();

            wyswietlenie();
        }

        private void slider2_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            slider2l.Content = "Kontrast: " + (int)slider2.Value;
            kopiowanie();

            wyswietlenie();
        }

        private void Button3_Click(object sender, RoutedEventArgs e)
        {
            pixelData.CopyTo(opixelData, 0);
            postep.Content = "Zapisano";
            slider1.Value = 0;
            slider2.Value = 0;
        }

        private void Odwrot_Click(object sender, RoutedEventArgs e)
        {
            for (int i = 0; i < opixelData.Length; ++i)
            {
                if (i % 4 == 3)
                    continue;
                opixelData[i] ^= 255;
            }
            kopiowanie();

            wyswietlenie();
        }

        private void rgbHist_Click(object sender, RoutedEventArgs e)
        {
            histogram(hist1, opixelData);
            wyswietlenie();
        }

        private void hist_Clicked(object sender, RoutedEventArgs e)
        {
            if (hist.IsChecked == false)
            {
                hist1.Background = new SolidColorBrush(Color.FromArgb(0, 0, 0, 0));
                hist1.Children.Clear();
                hist2.Background = new SolidColorBrush(Color.FromArgb(0, 0, 0, 0));
                hist2.Children.Clear();
            }
            else if (hist.IsChecked == true)
            {
                histogram(hist1, opixelData);
                wyswietlenie();
            }
        }

        private void fSr_Click(object sender, RoutedEventArgs e)
        {
            int wsp = Convert.ToInt16(wspSr.Text);
            int wsp2 = (wsp - 1) / 2;
            Task t = Task.Run(() =>
            {
                //                for (int i = 0; i < pixelData.Length; i++)
                Parallel.For(0, pixelData.Length, i =>
                {
                    //if (i % 4 == 3)
                    //    continue;
                    if (i % 4 == 3)
                    {

                    }
                    else
                    {
                        int suma = 0;
                        int count = 0;
                        for (int y = -wsp2; y <= wsp2; y++)
                        {
                            for (int x = -wsp2; x <= wsp2; x++)
                            {
                                int obl = i + w * x * 4 + y * 4;
                                if (obl > 0 && obl < pixelData.Length)
                                {
                                    suma += opixelData[obl];
                                    count++;
                                }
                            }
                        }
                        pixelData[i] = (byte)(suma / count);
                    }
                    //Dispatcher.Invoke(() =>
                    //{
                    //    postep2++;
                    //    postep.Content = (int)((1 - ((pixelData.Length - postep2) / (float)pixelData.Length)) * 100) + 1 + "%";
                    //});
                });
                Dispatcher.Invoke(() =>
                {
                    postep.Content = "Średnia aryt. ukończono";
                    wyswietlenie();
                });
            });
        }

        private void fMe_Click(object sender, RoutedEventArgs e)
        {
            int wsp = Convert.ToInt16(wspMe.Text);
            int wsp2 = (wsp - 1) / 2;
            //            int[] tab = new int[wsp * wsp];
            Task t = Task.Run(() =>
            {
                Parallel.For(0, pixelData.Length, i =>
                {
                    if (i % 4 == 3)
                    {

                    }
                    else
                    {
                        int[] tab = new int[wsp * wsp];
                        int count = 0;
                        for (int y = -wsp2; y <= wsp2; y++)
                        {
                            for (int x = -wsp2; x <= wsp2; x++)
                            {
                                int obl = i + w * x * 4 + y * 4;
                                if (obl > 0 && obl < pixelData.Length)
                                {
                                    tab[count] = opixelData[obl];
                                    count++;
                                }
                            }
                        }

                        for (int k = 0; k < count; k++)
                        {
                            Array.Sort(tab, 0, count);
                        }

                        if (count % 2 == 0)
                        {
                            pixelData[i] = (byte)((tab[count / 2] + tab[count / 2 + 1]) / 2);
                        }
                        else
                        {
                            pixelData[i] = (byte)(tab[count / 2 + 1]);
                        }
                    }
                    //Dispatcher.Invoke(() =>
                    //{
                    //    postep.Content = (int)((1 - ((pixelData.Length - i) / (float)pixelData.Length)) * 100) + 1 + "%";
                    //});
                });
                Dispatcher.Invoke(() =>
                {
                    postep.Content = "Mediana ukończono";
                    wyswietlenie();
                });
            });
        }

        private void fS3_Click(object sender, RoutedEventArgs e)
        {
            int wsp = Convert.ToInt16(wspSx.Text);
            int wsp2 = (wsp - 1) / 2;
            int[] tab = new int[wsp * wsp];
            tab[0] = Convert.ToInt16(wspS0.Text);
            tab[1] = Convert.ToInt16(wspS1.Text);
            tab[2] = Convert.ToInt16(wspS2.Text);
            tab[3] = Convert.ToInt16(wspS3.Text);
            tab[4] = Convert.ToInt16(wspS4.Text);
            tab[5] = Convert.ToInt16(wspS5.Text);
            tab[6] = Convert.ToInt16(wspS6.Text);
            tab[7] = Convert.ToInt16(wspS7.Text);
            tab[8] = Convert.ToInt16(wspS8.Text);
            Task t = Task.Run(() =>
            {
                //                for (int i = 0; i < pixelData.Length; i++)
                Parallel.For(0, pixelData.Length, i =>
                {
                    //if (i % 4 == 3)
                    //    continue;
                    if (i % 4 == 3)
                    {

                    }
                    else
                    {
                        int tabH = -1;
                        int suma = 0;
                        int count = 0;
                        for (int y = -wsp2; y <= wsp2; y++)
                        {
                            for (int x = -wsp2; x <= wsp2; x++)
                            {
                                tabH++;
                                int obl = i + w * x * 4 + y * 4;
                                if (obl > 0 && obl < pixelData.Length)
                                {
                                    suma += opixelData[obl] * tab[tabH];
                                    count++;
                                }
                            }
                        }

                        suma = (suma < 0) ? 0 : suma / count;
                        pixelData[i] = (suma > 255) ? (byte)255 : (byte)suma;
                    }
                    //Dispatcher.Invoke(() =>
                    //{
                    //    postep2++;
                    //    postep.Content = (int)((1 - ((pixelData.Length - postep2) / (float)pixelData.Length)) * 100) + 1 + "%";
                    //});
                });
                Dispatcher.Invoke(() =>
                    {
                        postep.Content = "S3 ukończono";
                        wyswietlenie();
                    });
            });
        }

        private void fRei_Click(object sender, RoutedEventArgs e)
        {
            int[] rTab = new int[256];
            int[] gTab = new int[256];
            int[] bTab = new int[256];

            for (int i = 0; i < 256; i++)
            {
                rTab[i] = 0;
                gTab[i] = 0;
                bTab[i] = 0;
            }

            for (int i = 0; i < pixelData.Length; i++)
            {
                if (i % 4 == 3)
                    continue;
                else if (i % 4 == 2)
                    bTab[pixelData[i]]++;
                else if (i % 4 == 1)
                    gTab[pixelData[i]]++;
                else if (i % 4 == 0)
                    rTab[pixelData[i]]++;
            }

            int bcount = 0;
            int rcount = 0;
            int gcount = 0;
            for (int i = 0; i < 256; i++)
            {
                if (bTab[i] != 0)
                {
                    bcount += bTab[i];
                    bTab[i] = bcount;
                }

                if (gTab[i] != 0)
                {
                    gcount += gTab[i];
                    gTab[i] = gcount;
                }

                if (rTab[i] != 0)
                {
                    rcount += rTab[i];
                    rTab[i] = rcount;
                }
            }

            for (int i = 0; i < pixelData.Length; i++)
            {
                if (i % 4 == 3)
                    continue;
                else if (i % 4 == 2)
                    pixelData[i] = (byte)((bTab[(int)pixelData[i]] / (float)(h * w)) * 255);
                else if (i % 4 == 1)
                    pixelData[i] = (byte)((gTab[(int)pixelData[i]] / (float)(h * w)) * 255);
                else if (i % 4 == 0)
                    pixelData[i] = (byte)((rTab[(int)pixelData[i]] / (float)(h * w)) * 255);
            }

            postep.Content = "Raleigh ukończono";
            wyswietlenie();
        }

        private void fKir_Click(object sender, RoutedEventArgs e)
        {
            List<int[]> tabL = new List<int[]>();
            int[] tab0 = new int[] { 5, 5, 5, -3, 0, -3, -3, -3, -3 };
            int[] tab1 = new int[] { 5, 5, -3, 5, 0, -3, -3, -3, -3 };
            int[] tab2 = new int[] { 5, -3, -3, 5, 0, -3, 5, -3, -3 };
            int[] tab3 = new int[] { -3, -3, -3, 5, 0, -3, 5, 5, -3 };
            int[] tab4 = new int[] { -3, -3, -3, -3, 0, -3, 5, 5, 5 };
            int[] tab5 = new int[] { -3, -3, -3, -3, 0, 5, -3, 5, 5 };
            int[] tab6 = new int[] { -3, -3, 5, -3, 0, 5, -3, -3, 5 };
            int[] tab7 = new int[] { -3, 5, 5, -3, 0, 5, -3, -3, -3 };
            tabL.Add(tab0);
            tabL.Add(tab1);
            tabL.Add(tab2);
            tabL.Add(tab3);
            tabL.Add(tab4);
            tabL.Add(tab5);
            tabL.Add(tab6);
            tabL.Add(tab7);

            Byte[] pixelData2 = new Byte[pixelData.Length];
            for (int i = 0; i < pixelData2.Length; i++)
            {
                pixelData2[i] = 0;
            }

            Task t = Task.Run(() =>
            {
                //                for (int i = 0; i < pixelData.Length; i++)
                foreach (int[] tab in tabL)
                {
                    Parallel.For(0, pixelData.Length, i =>
                    {
                        //if (i % 4 == 3)
                        //    continue;
                        if (i % 4 == 3)
                        {
                            pixelData2[i] = pixelData[i];
                        }
                        else
                        {
                            int tabH = -1;
                            int suma = 0;
                            int count = 0;
                            for (int y = -1; y <= 1; y++)
                            {
                                for (int x = -1; x <= 1; x++)
                                {
                                    tabH++;
                                    int obl = i + w * x * 4 + y * 4;
                                    if (obl > 0 && obl < pixelData.Length)
                                    {
                                        suma += opixelData[obl] * tab[tabH];
                                        count++;
                                    }
                                }
                            }

                            suma = (suma < 0) ? 0 : suma / count;
                            suma = (suma > 255) ? 255 : suma;
                            pixelData2[i] = ((byte)suma > pixelData2[i]) ? (byte)suma : pixelData2[i];
                        }
                    });
                }
                Dispatcher.Invoke(() =>
                {
                    postep.Content = "Kirsch ukończono";
                    pixelData2.CopyTo(pixelData, 0);
                    wyswietlenie();
                });
            });
        }

        private void fPor_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog op = new OpenFileDialog();
            op.Title = "Select a picture";
            op.Filter = "All supported graphics|*.jpg;*.jpeg;*.png;*.bmp|" +
                "JPEG (*.jpg;*.jpeg)|*.jpg;*.jpeg|" +
                "Portable Network Graphic (*.png)|*.png";
            if (op.ShowDialog() == true)
            {
                BitmapSource bmpS2 = new BitmapImage(new Uri(op.FileName));

                BitmapSource prgbaSource = new FormatConvertedBitmap(bmpS2, PixelFormats.Pbgra32, null, 0);
                WriteableBitmap wrBmp2 = new WriteableBitmap(prgbaSource);
                int w2 = wrBmp2.PixelWidth;
                int h2 = wrBmp2.PixelHeight;
                Byte[] porData = new byte[w2 * h2 * 4];
                wrBmp2.CopyPixels(porData, widthInBytes, 0);

                oblPodobienstwa(porData, pixelData, pod1);
            }
        }
    }
}
